<?php
/**
 * Zead
 *
 * LICENSE	
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://www.opensource.org/licenses/bsd-license.php
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to eu@marcelomx.com so we can send you a copy immediately.
 *
 * @category   Zead
 * @author     Marcelo Rodrigues Gonzaga <eu@marcelomx.com>
 * @copyright  Copyright (c) 2008 Marcelo Rodrigues Gonzaga
 * @link       http://code.google.com/p/zead 
 * @license    http://www.opensource.org/licenses/bsd-license.php     New BSD License
 *
 **/

/**
 *
 * @category   Zead
 * @package    Zead_Uri
 * @copyright  Copyright (c) 2008 Marcelo Rodrigues Gonzaga 
 * @license    http://www.opensource.org/licenses/bsd-license.php     New BSD License  
 */
class Zead_Uri
{
	protected $_schemeSpecific = '';
	
	protected $_scheme      = '';
	protected $_username    = '';
	protected $_password    = '';
	protected $_host        = '';
	protected $_port        = '';
	protected $_path        = '';
	protected $_query       = '';
	protected $_fragment    = '';
	
	/**
	 * Class constructor
	 * 
	 * @param string $schemeSpecific
	 */
	public function __construct($schemeSpecific) 
	{
		$this->_schemeSpecific = $schemeSpecific;
		$this->_parse($schemeSpecific);
	}
	
	/**
	 * Analisa e decompoe a URI em partes
	 * 
	 * @param string $schemeSpecific 
	 */
	protected function _parse($schemeSpecific) 
	{
		$uri = parse_url($schemeSpecific);
		$this->_scheme      = $uri['scheme'];
		$this->_username    = $uri['user'];
		$this->_password    = $uri['password'];
		$this->_host        = $uri['host'];
		$this->_port        = $uri['port'];
		$this->_path        = $uri['path'];
		$this->_query       = $uri['query'];
		$this->_fragment    = $uri['fragment'];
	}
	
	/**
	 * Return the formatted URI string
	 * @return string
	 */
	public function getUri()
	{
		if (strlen($this->_scheme)) {
			$uri = $this->_scheme ."://";
		}
		if (strlen($this->_username)) {
			$uri .= $this->_username;
			if (strlen($this->_password))
				$uri .= ':' . $this->_password . '@';
		}
		if (strlen($this->_host)) {
			$uri .= $this->_host;
		}
		if (strlen($this->_port)) {
			$uri .= ":". $this->_port;
		}
		$uri .= $this->_path;
				
		if (strlen($this->_query)) {
			$uri .= "?". $this->_query;
		}
		if (strlen($this->_fragment)) {
			$uri .= "#". $this->_fragment;
		}
		
		return $uri;
	}	
			
	
	/**
	 * Resolve paths to uri path from basename argument
	 * @param string $baseName
	 * @return void
	 */
	public function resolve($basePath)
	{			
		if ( strlen($this->_host) )
			return;

		// If has hash
		if ( preg_match("/^\#(.*?)$/ie",$this->_schemeSpecific) ) {
			$this->_parse($basePath . '#'. $this->fetchFragment());			
			return;
		}
		// Absolute path
		if ( substr($this->_path,0,1) == '/' )
			return;
			
		// Remove right slash
		$basePath = rtrim($basePath, '/');
					
		// Symbolic path
		if ( substr($this->_path,0,2) == './' ) {
			$this->_parse(($basePath != '' ? $basePath . '/' : '') . substr($this->_path,2));
			return;
		}
		// If path is a simple path
		if ( !preg_match("/\//ies", $this->_path) ) {
			$this->_path = ($basePath != '' ? $basePath .'/' : '') . $this->_path;
			return;
		}
		// Relative path
		$filePaths  = explode("/", $this->_path);
		$basePaths = explode("/", $basePath);
		$countLevels = array_count_values($filePaths);
		$count = (int) $countLevels['..'];
		
		$basePaths = implode('/', array_slice($basePaths, 0, count($basePaths) - $count));
		$filePaths  = implode('/', array_slice($filePaths, $count, count($filePaths)));
		
		$this->_parse($basePaths . '/' . $filePaths, true);
		
		return $this;
	}
	
	/**
	 * Metodo 
	 * 
	 * @param string $schemeSpecific
	 * @param string $basePath
	 * @param array  $bindQuery
	 * @param bool   $mergeQuery
	 * @return string
	 */
	public static function filter($schemeSpecific, $basePath = null, $bindQuery = array(), $mergeQuery = false)
	{
		$instance = new self($schemeSpecific);
		
		if ($basePath != null) {
			$instance->resolve($basePath);
		}
		if (count($bindQuery)) {
			$instance->setQuery($bindQuery, $mergeQuery);
		}
		
		return $instance->getUri();
	}
		
	
	/**
	 * Retorna a URI como uma string
	 * @return string
	 */
	public function __toString() 
	{
		return (string) $this->getUri();
	}	
	
	/**
	 * Set the URI scheme
	 * @param  string $scheme
	 * @return Zead_Uri
	 */
	public function setScheme($scheme)
	{
		$this->_scheme = $scheme;
		return $this;
	}
	
	/**
	 * Return the scheme
	 * @return string
	 */
	public function getScheme()
	{
		return $this->_scheme;
	}

	/**
	 * Set the URI username
	 * @param  string $username
	 * @return Zead_Uri
	 */
	
	public function setUsername($username)
	{
		$this->_username = $username;
		return $this;
	}
	
	/**
	 * Return the username
	 * @return string
	 */	
	public function getUsername()
	{
		return $this->_username;	
	}

	/**
	 * Set the URI password
	 * @param  string $password
	 * @return Zead_Uri
	 */
	
	public function setPassword($password)
	{
		$this->_password = $password;
		return $this;
	}
	
	/**
	 * Return the scheme
	 * @return string
	 */
	public function getPassword()
	{
		return $this->_password;	
	}

	/**
	 * Set the URI host
	 * @param  string $host
	 * @return Zead_Uri
	 */
	
	public function setHost($host)
	{
		$this->_scheme = $host;
		return $this;
	}

	/**
	 * Return the scheme
	 * @return string
	 */
	public function getHost()
	{
		return $this->_host;	
	}
	
	/**
	 * Set the URI port
	 * @param  string $port
	 * @return Zead_Uri
	 */
	
	public function setPort($port)
	{
		$this->_port = $port;
		return $this;
	}
	
	/**
	 * Return the scheme
	 * @return string
	 */
	public function getPort()
	{
		return $this->_port; 
	}
	
	/**
	 * Set the URI path
	 * @param  string $path
	 * @return Zead_Uri
	 */
	
	public function setPath($path)
	{
		$this->_path = $path;
		return $this;
	}
	
	/**
	 * Return the scheme
	 * @return string
	 */
	public function getPath()
	{
		return $this->_path;	
	}
	
	/**
	 * Retorna informações sobre o path
	 * @retur string|array
	 */
	public function getPathInfo($key = null)
	{
		$info = pathinfo($this->getPath());
		
		if ($key !== null) {
			return $info[$key];
		}
		
		return $info;
	}
	
	/**
	 * Seta os parametros HTTP da URI atual
	 * 
	 * @param string|array $query
	 * @param bool $merge
	 * @return Zead_Uri
	 */
	public function setQuery($query, $merge = false) 
	{
		if (is_array($query)) {
			if ($merge !== false) {
				parse_str($this->_query, $oldQuery);
				$query = array_merge($oldQuery, $query);
			}
			$query = http_build_query($query);
		}
		$this->_query = $query;
			
		return $this;		
	}
	
	/**
	 * Adiciona uma parametro a lista de parametros HTTP da URI atual.
	 * 
	 * @param mixed $param
	 * @param mixed $value
	 * @return Zead_Uri
	 */
	public function bind($param, $value = null) 
	{
		parse_str((string) $this->_query, $query);
		
		if (is_array($param)) {
			$query[] = $param;
		} else {
			$query[$param]=$value;
		}
		
		$this->setQuery($query);
		
		return $this;
	}	
	
	/**
	 * Retorna os parametros HTTP da URI atual.
	 * @return Zead_Uri
	 */
	public function getQuery() 
	{	
		return $this->_query;
	}	
	
	/**
	 * Set the URI fragment
	 * @param  string $fragment
	 * @return Zead_Uri
	 */
	public function setFragment($fragment)
	{
		$this->_fragment = $fragment;
		return $this;	
	}
	
	/**
	 * Return the scheme
	 * @return string
	 */
	public function getFragment()
	{
		return $this->_fragment;
	}
}
